#include "cutscene.h"
#include "cutscene_transition.h"
#include "camera/fb_camera.h"
#include "ui/ui_manager.h"
#include "font/font.h"

#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		

#include "application/game_application.h"
#include "application/game_rms.h"

#include "lang/lang.h"
#include "audio/audio.h"

#include "script/scripthelper.h"


#include "background/bg_scene.h"
#include "background/bg_editor.h"

//image cache
#include "graphics/image_cache.h"
#include "graphics/graphics_util.h"

static const float GAME_SIZE_X = 3.0f; //2.0f;
static const float GAME_SIZE_Y = 3.0f; //2.0f;
static const float BGTILE_SIZE = 1024.0f;

using namespace script;
using namespace gameflow;

Cutscene::Cutscene() : StateHolder(), 
	m_pCache(0), m_pUIPanel(0), m_pUIStoryText(0), m_pUIStoryTextBG(0), m_pUIStoryControls(0), 
	m_pEntryTransition(0), m_pExitTransition(0), m_pCurrentScene(0), m_pQueuedScene(0),
	m_FrameTime(0), m_ForegroundScale(1.2f), m_BackgroundScale(1.2f)
{
	m_pUIPanel = ui::UIManager::Instance().GetWidget("story");
	if (!m_pUIPanel)
	{
		m_pUIPanel = ui::UIManager::Instance().AddPanel("ui/story.ui");
	}

	if (m_pUIPanel)
	{
		m_pUIStoryTextBG = m_pUIPanel->GetWidgetByName("story_text_container");
		m_pUIStoryText = m_pUIPanel->GetWidgetByName("story_text");		
		m_pUIStoryTitle = m_pUIPanel->GetWidgetByName("story_title");
		m_pUIStoryControls = m_pUIPanel->GetWidgetByName("story_controls");
		if (m_pUIStoryControls)
		{
			m_pUIStoryBack = m_pUIStoryControls->GetWidgetByName("btn_left");
			m_pUIStorySkip = m_pUIStoryControls->GetWidgetByName("btn_skip");
			m_pUIStoryNext = m_pUIStoryControls->GetWidgetByName("btn_right");
		}
	}
}

Cutscene::~Cutscene()
{
	// hide our panel
	if (m_pUIPanel)
		m_pUIPanel->SetIsVisible(false);

	//done automatically
	UTIL_SAFE_DELETE(m_pCache);

	for (int i = 0; i < (int)m_CutSceneData.size(); i++)
	{
		CutSceneData* pCutScene = m_CutSceneData[i];
		UTIL_SAFE_DELETE(pCutScene);
	}
	m_CutSceneData.clear();
}

void Cutscene::Reload(const std::string& File)
{
	Close();
	Load(File);
	LuaPlus::LuaObject	cutscenetable = GetTable("cutscene");
	if (cutscenetable.IsTable())
	{
		m_CutSceneData.clear();

		for (int i = 0; i < cutscenetable.GetTableCount(); i++)
		{
			
			LuaPlus::LuaObject	Obj = cutscenetable.GetByIndex(i+1);
			if (!Obj.IsTable()) continue;

			Cutscene::CutSceneData *pCutsceneData = new CutSceneData();

			pCutsceneData->Name				= ScriptHelper::GetTableString(Obj,"Name");
			pCutsceneData->Title			= ScriptHelper::GetTableString(Obj,"Title","");
			pCutsceneData->BGScene			= ScriptHelper::GetTableString(Obj,"BGScene");
			pCutsceneData->Text				= ScriptHelper::GetTableString(Obj,"Text");
			pCutsceneData->From.x			= ScriptHelper::GetTableFloat(Obj,"FromX") * GraphicsUtil::InvW;
			pCutsceneData->From.y			= ScriptHelper::GetTableFloat(Obj,"FromY") * GraphicsUtil::InvH;
			pCutsceneData->To.x				= ScriptHelper::GetTableFloat(Obj,"ToX")* GraphicsUtil::InvW;
			pCutsceneData->To.y				= ScriptHelper::GetTableFloat(Obj,"ToY")* GraphicsUtil::InvH;
			pCutsceneData->PanDuration		= ScriptHelper::GetTableFloat(Obj,"PanDuration",1.0f);
			pCutsceneData->EnterEffect		= ScriptHelper::GetTableString(Obj,"EnterEffect","none");
			pCutsceneData->ExitEffect		= ScriptHelper::GetTableString(Obj,"ExitEffect","none");
			pCutsceneData->SceneDuration	= ScriptHelper::GetTableFloat(Obj,"SceneDuration");
			pCutsceneData->Next				= ScriptHelper::GetTableString(Obj,"Next");
			pCutsceneData->Prev				= ScriptHelper::GetTableString(Obj,"Prev");
			pCutsceneData->Voiceover		= ScriptHelper::GetTableString(Obj,"Voiceover");
			//optional params
			pCutsceneData->ScaleDuration	= ScriptHelper::GetTableFloat(Obj,"ScaleDuration",0.5f);
			pCutsceneData->ScaleFrom		= ScriptHelper::GetTableFloat(Obj,"ScaleFrom",1.2f);
			pCutsceneData->ScaleTo			= ScriptHelper::GetTableFloat(Obj,"ScaleTo",1.2f);
			pCutsceneData->AutoAdvance		= ScriptHelper::GetTableInt(Obj,"AutoAdvance",0);

			m_CutSceneData.push_back(pCutsceneData);
		}
	} else
	{
		_LOG(MSG_ERROR, "unable to load cutscene objects from " << File);
		return;
	}
	if (m_CutSceneData.empty()) 
	{
		_LOG(MSG_ERROR, "cutscene object file empty (" << File);
	}
	m_pCurrentScene = m_CutSceneData[0];
	ClearScreen();
	SetPage(m_pCurrentScene->Name);
	Refresh(true);
	
	//make sure no transitions are allocated
	UTIL_SAFE_DELETE(m_pEntryTransition);
	UTIL_SAFE_DELETE(m_pExitTransition);

	//replace all instances of %PARAM% with the actual player name
	//const std::string name = g_pApp->GetRMS()->GetNameFromID(g_pApp->GetRMS()->GetCurrentProfile());
	//ui::UIManager::Instance().AddReplaceWord("%PARAM%", name);

	//gameflow::GameFlowManager::Instance().S
}

void Cutscene::Init()
{	
	ClearScreen();	

	//setup image cache
	UTIL_SAFE_DELETE(m_pCache);
	m_pCache = GetGraphics()->CreateCache(1024, 1024, 32);	

	Refresh(true);

	if (m_pUIStoryControls)	m_pUIStoryControls->SetIsActive(true);
}

void	Cutscene::GetStartPoints(std::vector<std::string>& List)
{
	List.resize(0);
	for (unsigned int i=0; i<m_CutSceneData.size(); i++)
	{
		CutSceneData* pData = m_CutSceneData[i];
		if (pData->Prev.empty())
		{
			List.push_back(pData->Name);	// this is a starting-point
		}
	}
}

void Cutscene::ClearScreen()
{
	m_Background.DeleteObjects();
	m_Background.FlushImages();
	m_Background.SetBackground(GetBlankImg());
	m_Background.SetOffset(math::Vec2(0,0));

	UpdateStoryText("");
}

bool Cutscene::Tick(float dt)
{	
	m_FrameTime+=dt;

	//process panning and scaling and autoadvance
	if (m_pCurrentScene)
	{

		float		t1 = m_FrameTime / (m_pCurrentScene->PanDuration > 0 ? m_pCurrentScene->PanDuration : 1);
		UTIL_CLAMP(t1, 0, 1);
		m_Background.SetOffset(m_pCurrentScene->From  + ((m_pCurrentScene->To-m_pCurrentScene->From) * t1));

		float		t2 = m_FrameTime / (m_pCurrentScene->ScaleDuration > 0 ? m_pCurrentScene->ScaleDuration : 1);
		UTIL_CLAMP(t2, 0, 1);
		m_BackgroundScale = m_ForegroundScale = m_pCurrentScene->ScaleFrom  + ((m_pCurrentScene->ScaleTo-m_pCurrentScene->ScaleFrom) * t2);

		if (m_pCurrentScene->AutoAdvance && t1 >= 1 && t2 >= 1)
		{
			NextPage();
		}
	}	

	m_Background.Tick(dt);	

	//process transition
	if (m_pExitTransition)
	{
		if (m_pUIStoryControls)
		{
			m_pUIStoryControls->SetIsActive(false);
			//m_pUIStoryControls->SetIsVisible(false);
		}
		if (m_pUIStoryTextBG) m_pUIStoryTextBG->SetIsVisible(false);

		m_pExitTransition->Tick(dt);
		if (m_pExitTransition->IsReady())
		{
			OnTransitionComplete(m_pExitTransition);
			UTIL_SAFE_DELETE(m_pExitTransition);
		}
	}
	else
	if (m_pQueuedScene)
	{
		m_FrameTime = 0;
		bool reset = (m_pQueuedScene->BGScene != m_pCurrentScene->BGScene);
		m_pCurrentScene = m_pQueuedScene;		
		Refresh(reset);
		
		if (!m_pCurrentScene->Voiceover.empty())
		{
			AUDIO().PlayEvent(m_pCurrentScene->Voiceover);
		}
		m_pQueuedScene = NULL;
	}
	else
	if (m_pEntryTransition)
	{
		m_pEntryTransition->Tick(dt);
		if (m_pEntryTransition->IsReady())
		{
			if (m_pUIStoryControls)
			{
				m_pUIStoryControls->SetIsActive(true);
				//m_pUIStoryControls->SetIsVisible(true);
			}

			OnTransitionComplete(m_pEntryTransition);
			UTIL_SAFE_DELETE(m_pEntryTransition);
		}
	}
	else if (m_pUIStoryControls)
	{
		m_pUIStoryControls->SetIsActive(true);
	}
	
	if (m_Background.GetEditor()) 
	{
		if (m_pUIPanel)
		{
			m_pUIPanel->SetIsVisible(false);
			m_pUIPanel->SetIsActive(false);
		}
		return true;
	}
	else
	if (m_pUIPanel)
	{
		m_pUIPanel->SetIsVisible(true);
		m_pUIPanel->SetIsActive(true);
	}

	if (!m_Background.GetEditor() && m_pCache->BeginCache())//
	{				
		//render BGSCene to ImageCache
		m_pCache->ClearCache( ColorRGBA(255,255,255,0) );	
		m_Background.RenderBackground(m_BackgroundScale);
		m_Background.RenderForeground(m_ForegroundScale);
		m_pCache->EndCache();
	}

	return true;
}

bool Cutscene::Render()
{
	if (m_Background.GetEditor())
	{
		m_Background.RenderBackground(1.0f);
		m_Background.RenderForeground(1.0f);
	}
	else
	RenderCache(m_pCache, 0.05f, 0.05f, false);

	if (m_pExitTransition)
	{
		m_pExitTransition->Render();
	}
	else
	if (m_pEntryTransition)
	{
		m_pEntryTransition->Render();
	}

	return true;
}

bool Cutscene::SetPage(const std::string& scene)
{
	if (m_pEntryTransition || m_pExitTransition) return true;

	//m_FrameTime = 0;
	for (int i = 0; i < (int)m_CutSceneData.size(); i++)
	{
		CutSceneData* pNextScene = m_CutSceneData[i];
		
		if (!pNextScene) continue;

		if (strcmpi(pNextScene->Name.c_str(), scene.c_str())==0)
		{		
			m_pQueuedScene = pNextScene;

			//make sure no transitions are allocated
			UTIL_SAFE_DELETE(m_pEntryTransition);
			UTIL_SAFE_DELETE(m_pExitTransition);

			m_pExitTransition = CutsceneTransition::GetTransition(m_pCurrentScene->ExitEffect);
			m_pEntryTransition = CutsceneTransition::GetTransition(pNextScene->EnterEffect);

 			//if (m_pUIStoryTextBG) m_pUIStoryTextBG->AddUIPartFadeMover(math::Vec2(0,0.2f),math::Vec2(0.0f,1.0f),"");

			if (m_pUIStoryControls)
			{
				if (pNextScene->AutoAdvance == 1)
				{
					m_pUIStoryControls->SetIsActive(false);
					//m_pUIStoryControls->SetIsVisible(false);
				}
				else
				{
					m_pUIStoryControls->SetIsActive(true);
					//m_pUIStoryControls->SetIsVisible(true);
				}
				
				if (m_pUIStoryBack)
				{
					if (!pNextScene || (strcmpi(pNextScene->Prev.c_str(), "")==0))
					{
						m_pUIStoryBack->SetIsVisible(false);
					}
					else 
					{
						m_pUIStoryBack->SetIsVisible(true);
					}
				}
			}

			return true;
		}
	}

	return false;
}

bool Cutscene::PrevPage()
{
	if (!m_pCurrentScene || (strcmpi(m_pCurrentScene->Prev.c_str(), "")==0))
	{
		return false;
	}	

	return SetPage(m_pCurrentScene->Prev);
}

bool Cutscene::NextPage()
{
	if (!m_pCurrentScene || (strcmpi(m_pCurrentScene->Next.c_str(), "END")==0))
	{
		EndScene();
		return false;
	}	

	return SetPage(m_pCurrentScene->Next);
}

void Cutscene::EndScene()
{
	if (m_pUIStoryControls)
	{
		m_pUIStoryControls->SetIsActive(false);
		//m_pUIStoryControls->AddUIPartFadeMover(math::Vec2(0,0.3f),math::Vec2(1.0f,0.0f),"");
	}
}

void Cutscene::Refresh(bool resetbg)
{
	if (!m_pCurrentScene) return;

	UpdateStoryText(m_pCurrentScene->Text,m_pCurrentScene->Title);	

	if (resetbg)
	{
		m_Background.FlushImages();
		m_Background.DeleteObjects();
		std::string		Scene = "cutscene/"+m_pCurrentScene->BGScene+".lua";
		LuaPlus::LuaState* pState = ScriptHelper::CreateLuaState();
		if (!ScriptHelper::DoFile(*pState, Scene))
		{
			// not a valid Luastate.. let's see if it's a valid image or sprite instead
			//LuaState::Destroy( m_pState );	
			Close(); // cw does not like LuaState::Destroy... let's just call Close()
			ui::UISprite		TestSprite;
			TestSprite.Create(m_pCurrentScene->BGScene);
			if (TestSprite.IsValid())
			{
				m_Background.SetBackground(m_pCurrentScene->BGScene);
			}

		}  else
		{
			m_Background.LoadFile(*pState);
		} 
		m_Background.SetOffset(m_pCurrentScene->From);
	}
}

void Cutscene::UpdateStoryText(const std::string& storytext, const std::string& storytitle)
{
	if (!m_pUIStoryText) return;

	const std::string name = g_pApp->GetRMS()->GetNameFromID(g_pApp->GetRMS()->GetCurrentProfile());
	m_pUIStoryText->SetTextW(
		lang::Lang::CreateParamStringA(storytext, 1, name.c_str())
	);

	if (m_pUIStoryTitle)
	{
		if (storytitle == "")
		{
			m_pUIStoryTitle->SetIsVisible(false);
		}
		else
		{
			m_pUIStoryTitle->SetTextA(storytitle);
			m_pUIStoryTitle->SetIsVisible(true);
		}
	}

	//resize UI
	if (m_pUIStoryText && m_pUIStoryTextBG)
	{
		const std::string Text = m_pUIStoryText->GetTextA();//pMsg->ShowAltText() ? pMsg->GetAltText() : pMsg->GetText();
		std::wstring WText = m_pUIStoryText->GetText();//lang::StrMan::Instance().GetStringA(Text);
		math::Vec2 Size = m_pUIStoryText->GetSize();
		ui::UIText* pText = m_pUIStoryText->GetTextObj();
		if (WText.empty())
			WText = lang::Lang::ConvertAtoW(Text);

		if (Text == "")
		{
			m_pUIStoryTextBG->SetIsVisible(false);
			return;
		}
		else
		{
			m_pUIStoryTextBG->SetIsVisible(true);
		}

		assert(pText);

		// we need to resize (height only) the text widget based on text contents
		if (Font* pFont = pText->GetFont())
		{
			math::Vec2 From, TextSize, TextBoxSize, TextBoxDiff, ButtonSize, ButtonDiff;
			math::Vec2 BoxSize = Size;
			BoxSize.x -= 2.0f * pText->GetBorder().x;
			BoxSize.y = 1.0f;
			if (m_pUIStoryTextBG)
			{
				TextBoxSize = m_pUIStoryTextBG->GetSize();
				TextBoxDiff = TextBoxSize - Size;
			}
			pFont->GetTextExtent(WText, BoxSize, From, TextSize);
			Size.y = TextSize.y + (2.0f * pText->GetBorder().y);
			if (m_pUIStoryTextBG)
			{
				TextBoxSize.y = Size.y + TextBoxDiff.y;
				m_pUIStoryTextBG->SetSize(TextBoxSize);
			}
		}

		// display and resize
		m_pUIStoryText->SetSize(Size);
		m_pUIStoryText->SetTextW(WText);

		//m_pUIStoryTextBG->AddUIPartFadeMover(math::Vec2(0,0.2f),math::Vec2(0.0f,1.0f),"");
	}
}

void Cutscene::SetOffset(float from_x, float from_y)
{
	m_Background.SetOffset(math::Vec2(from_x,from_y));	
}

void Cutscene::RenderCache(ImageCache* pCache, float x, float y, bool blend)
{
	static Graphics::Vertex2D	CacheA[6];

	float Scale = 1.0f;
	float Rot = 0.0f;
	math::Vec2 Pos;
	Pos.x = x;
	Pos.y = y;

	if (pCache)
	{
		const math::Vec2	UV1 = math::Vec2(0.0f, 0.0f);
		const math::Vec2	UV2 = math::Vec2(1.0f, GraphicsUtil::HEIGHT * GraphicsUtil::InvW);

		//GraphicsUtil::FillRotQuad(Pos, math::Vec2(Scale, Scale), Rot, UV1, UV2, ColorRGBA(r, g, b, a), CacheA);
		GraphicsUtil::FillQuad(Pos, math::Vec2(0.9f, 0.9f),UV1,UV2, ColorRGBA((unsigned char)(255), (unsigned char)(255), (unsigned char)(255), 255), CacheA);		
		//GraphicsUtil::FillQuad(Pos, math::Vec2(0.8f, 0.8f),UV1,UV2, ColorRGBA(255,255,255,255), CacheA);		

		pCache->SetTextureStage(0);

		if (blend)
		{
			GetGraphics()->SetRenderState( Graphics::RS_SRC_BLEND,  Graphics::RV_BLEND_SRC_ALPHA );
			GetGraphics()->SetRenderState( Graphics::RS_DST_BLEND,  Graphics::RV_BLEND_ONE );
		}
		GetGraphics()->DrawVertex2D(CacheA, 6);
		if (blend)		// reset
		{
			GetGraphics()->SetRenderState( Graphics::RS_SRC_BLEND,  Graphics::RV_BLEND_SRC_ALPHA );
			GetGraphics()->SetRenderState( Graphics::RS_DST_BLEND,  Graphics::RV_BLEND_INVSRC_ALPHA );
		}
	}
}
